#include <ani.h>
#include <array>
#include <vector>
#include <iostream>

static void CallMeWithOptionalDouble([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object, ani_object double_object){
    
    ani_boolean isUndefined;
    env->Reference_IsUndefined(double_object, &isUndefined);
    if(isUndefined)
    {
        std::cout << "CallMeWithOptionalDouble NOT Pass Value " << std::endl;
        return;
    }
  
    ani_double result;
    if (ANI_OK !=env->Object_CallMethodByName_Double(double_object, "doubleValue", nullptr, &result)){
        std::cerr << "Object_CallMethodByName_Double Fail" << std::endl;
    }

    std::cout << "CallMeWithOptionalDouble get double " <<  result << std::endl;

}

static void CallMeWithOptionalString([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object, ani_string string_object){
    ani_boolean isUndefined;
    env->Reference_IsUndefined(string_object, &isUndefined);
    if(isUndefined)
    {
        std::cout << "CallMeWithOptionalString NOT Pass Value " << std::endl;
        return;
    }

    ani_size  strSize;
    env->String_GetUTF8Size(string_object, &strSize);
   
    std::vector<char> buffer(strSize + 1); // +1 for null terminator
    char* utf8_buffer = buffer.data();

    //String_GetUTF8 Supportted by https://gitee.com/openharmony/arkcompiler_runtime_core/pulls/3416
    ani_size bytes_written = 0;
    env->String_GetUTF8(string_object, utf8_buffer, strSize + 1, &bytes_written);
    
    utf8_buffer[bytes_written] = '\0'; 
    std::string content = std::string(utf8_buffer); 
    std::cout << "CallMeWithOptionalString Get "<< content << std::endl;
}


static void ReadOptionalField([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object){
    
    //this.optionField 现在是一个对象，要使用 Object_GetFieldByName_Ref
    ani_ref int_ref;
    if(ANI_OK != env->Object_GetFieldByName_Ref(object, "optionField", &int_ref)){
        std::cerr << "Object_GetFieldByName_Ref optionField Failed" << std::endl;
    }

    //判断是否是undefined 
    ani_boolean isUndefined;
    if(ANI_OK != env->Reference_IsUndefined(int_ref,&isUndefined)){
        std::cerr << "Object_GetFieldByName_Ref optionField Failed" << std::endl;
        return ;
    }

    if(isUndefined){
        std::cout << "optionField is Undefined Now" << std::endl;
        return;
    }

    //从Int对象中解出数值
    ani_int int_value;
    if(ANI_OK != env->Object_CallMethodByName_Int(static_cast<ani_object>(int_ref), "intValue", nullptr,&int_value)){
        std::cerr << "Object_GetFieldByName_Ref optionField Failed" << std::endl;
        return;
    }
    std::cout << "optionField is:" <<  int_value  << std::endl;
}


ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
{
    ani_env *env;
    if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
        std::cerr << "Unsupported ANI_VERSION_1" << std::endl;
        return ANI_ERROR;
    }

    static const char *className = "ani_optional_parameter.Foo";
   
    ani_class cls;
    if (ANI_OK != env->FindClass(className, &cls)) {
        std::cerr << "Not found '" << className << "'" << std::endl;
        return ANI_ERROR;
    }

    std::array methods = {
        ani_native_function {"CallMeWithOptionalDouble", nullptr, reinterpret_cast<void *>(CallMeWithOptionalDouble)},
        ani_native_function {"CallMeWithOptionalString", nullptr, reinterpret_cast<void *>(CallMeWithOptionalString)},
        ani_native_function {"ReadOptionalField", nullptr, reinterpret_cast<void *>(ReadOptionalField)},
    };

    std::cout << "Start bind native methods to '" << className << "'" << std::endl;

    if (ANI_OK != env->Class_BindNativeMethods(cls, methods.data(), methods.size())) {
        std::cerr << "Cannot bind native methods to '" << className << "'" << std::endl;
        return ANI_ERROR;
    };
    std::cout << "Finish bind native methods to '" << className << "'" << std::endl;

    *result = ANI_VERSION_1;
    return ANI_OK;
}